#include "defines.h"
#include "kozos.h"
#include "lib.h"
#include "consdrv.h"
#include "kinfo.h"
#include "netdef.h"
#include "netlib.h"
#include "ni.h"
#include "ping.h"

#define RECV_BUFFER_SIZE 64

static char *getarg(char **pp)
{
    char *p = *pp;
    char *q;

    while (*p == ' ') p++;
    if (*p == '\0') { *pp = p; return NULL; }
    q = p;
    while (*p != ' ' && *p != '\0') p++;
    if (*p == ' ') *p++ = '\0';

    *pp = p;
    return q;
}

static const char *COMMANDS[] = {"echo", "exit", "help", "niconf", "ping", "ps",
                                 "reset", "resource", "sleep", "time",
                                 NULL};

int command_main(int argc, char *argv[])
{
    int c;
    kz_msgbox_id_t msgbox_in;
    char *recv_buf;
    int recv_len;

    recv_buf = kz_kmalloc(RECV_BUFFER_SIZE);
    msgbox_in = kz_getmsgbox();
    cons_use(msgbox_in);

    while (1) {
        char *p, *q;

        puts("command> ");

        // コマンド行を得る
        recv_len = 0;
        while (1) {
            kz_recv(msgbox_in, &c, NULL);

            if (c == '\r') {
                putc('\n');
                recv_buf[recv_len++] = '\0';
                break;

            } else if (c == '\b') {
                if (recv_len) {
                    puts("\b \b");
                    recv_len--;
                }

            } else if (c == '\t') {
                ;

            } else if (recv_len < RECV_BUFFER_SIZE - 1) {
                putc(c);
                recv_buf[recv_len++] = c;
            }
        }

        // コマンド処理
        p = recv_buf;
        q = getarg(&p);
        if (!q) continue;

        if (!strcmp(q, "echo")) {
            q = getarg(&p);
            if (q) {
                puts(q);
                while (1) {
                    q = getarg(&p);
                    if (!q) break;
                    putc(' ');
                    puts(q);
                }
            }
            putc('\n');

        } else if (!strcmp(q, "sleep")) {
            int t;
#define SLEEP_MAX   100
            if ((q = getarg(&p)) && is_num(q) && (t = atoi(q)) >= 0 && t <= SLEEP_MAX)
            {

                kz_wait(SECS(t));

            } else {
                puts("invalid time. (0-");
                putval(SLEEP_MAX, 0);
                puts(")\n");
            }

        } else if (!strcmp(q, "help")) {
            const char **p;
            for(p = COMMANDS; *p; p++) {
                puts(*p);
                putc('\n');
            }

        } else if (!strcmp(q, "ps")) {
            kz_threadinfo_t *info, *p;

            puts("ID NAME PRIORITY STACKSIZE FLAGS\n");

            info = (kz_threadinfo_t *)kz_getthreads();

            for (p = info; p < info + THREAD_NUM; p++) {
                if (p->info.id == THREAD_ID_NULL)
                    break;

                putval(p->info.id, 0);
                putc(' ');
                if (p->is_current) putc('*');
                puts(p->info.name);
                putc(' ');
                putval(p->info.priority, 0);
                puts(" 0x");
                putxval(p->info.stacksize, 0);
                puts(" 0x");
                putxval(p->info.flags, 4);
                putc('\n');
            }

            kz_kmfree(info);

        } else if (!strcmp(q, "exit")) {
            break;

        } else if (!strcmp(q, "reset")) {
            kz_reset();

        } else if (!strcmp(q, "resource")) {
            char *p = kz_resource();
            kz_resourceinfo_t *q = (kz_resourceinfo_t *)p;
            int size;

            size = q->size - 1;
            q++;
            while (size--) {
                if (q->type == RESOURCE_THREAD) {
                    puts("THREAD: ");
                    putval(q->used, 0);
                    puts(" / ");
                    putval(q->num, 0);
                    puts("\n");
                } else if (q->type == RESOURCE_MEMORY) {
                    puts("MEM");
                    putval(q->size, 0);
                    puts(": ");
                    putval(q->used, 0);
                    puts(" / ");
                    putval(q->num, 0);
                    puts("\n");
                } else if (q->type == RESOURCE_MSGBOX) {
                    puts("MSGBOX: ");
                    putval(q->used, 0);
                    puts(" / ");
                    putval(q->num, 0);
                    puts("\n");
                }
                q++;
            }

            kz_kmfree(p);

        } else if (!strcmp(q, "time")) {
            uint32 H;
            uint16 M, S;

            H = div3216(kz_systime(), TICK_PER_SEC, NULL);
            H = div3216(H, 60, &S);
            H = div3216(H, 60, &M);

            putval(H, 2);
            putc(':');
            putval(M, 2);
            putc(':');
            putval(S, 2);
            putc('\n');

        } else if (!strcmp(q, "niconf")) {
            q = getarg(&p);

            if (q) {
                uint8 IP[4], gateway[4];
                int mask;

                if (parse_IP(IP, q) == -1)
                    goto fail;

                if (!(q = getarg(&p)) || !is_num(q))
                    goto fail;
                mask = atoi(q);

                if (!(q = getarg(&p)) || parse_IP(gateway, q) == -1)
                    goto fail;

                if (niconf_set(IP, mask, gateway) == -1)
                    goto fail;

                goto end;

fail:
                puts("Failed.\n");

end:
                ;

            } else {
                niconf_print();
            }

        } else if (!strcmp(q, "ping")) {
#define PING_DATA_SIZE 32
#define PING_WAIT 3
            uint8 dst_IP[4];

            q = getarg(&p);
            if (q) {
                if (parse_IP(dst_IP, q) == 0) {
                    ping_send(dst_IP, PING_DATA_SIZE);
                    kz_wait(SECS(PING_WAIT));
                } else {
                    puts("Bad address.\n");
                }
            } else {
                puts("No address.\n");
            }

        } else {
            puts("unknown command.\n");
        }
    }

    cons_close(msgbox_in);
    kz_freemsgbox(msgbox_in);
    kz_kmfree(recv_buf);

    return 0;
}
